package website.service.impl;

import cn.hutool.core.util.BooleanUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import website.constants.Code;
import website.domain.*;
import website.dto.ArticleDTO;
import website.mapper.*;
import website.service.*;
import website.utils.NowDateUtils;

import javax.annotation.Resource;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;

import static website.constants.RedisConstants.*;

@Service
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    @Resource
    private ArticleMapper articleMapper;

    @Resource
    private ClassifyMapper classifyMapper;

    @Resource
    private TagMapper tagMapper;

    @Autowired
    private UserService userService;

    @Autowired
    private CommentService commentService;

    @Autowired
    private WebService webService;

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 分页分类查询文章列表
     *
     * @param cid
     * @param current
     * @param size
     * @param articleDTO
     * @return
     */
    @Override
    public ResModel<Page<Article>> getList(Integer current, Integer size,Integer cid,  ArticleDTO articleDTO) {
        Page<Article> page = new Page<>(current, size);
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        //筛选
        if (cid!=null && cid!=-1){
            wrapper.eq(true,Article::getCid,cid);
        }
        //模糊搜索
        wrapper.like(articleDTO.getSelectText()!=null,Article::getArticleTitle,articleDTO.getSelectText());
        Page<Article> articlePage = articleMapper.selectPage(page, wrapper);
        List<Article> records = articlePage.getRecords();
        for (Article record : records) {
            ResModel count = commentService.getCount(record.getId());
            record.setComments((Integer) count.getData());
        }
        //更新网站浏览次数
        webService.update().setSql("visits = visits +1").update();
        return ResModel.success(Code.SUCCESS,articlePage);
    }

    /**
     * 查询文章详情
     * @param id
     * @return
     */
    @Override
    public ResModel<Article> getArticle(Integer id) {
        if (id==null){
            return ResModel.error(Code.FAIL);
        }
        Boolean isMember=false;
        if(UserHolder.getUser()!=null){
            Integer userId = UserHolder.getUser().getUid();
            String likeKey = BLOG_LIKED_KEY + id;
            isMember = redisTemplate.opsForSet().isMember(likeKey, userId.toString());
        }
        String key = CACHE_ARTICLE_KEY + id;
        //查询缓存
        String json = redisTemplate.opsForValue().get(key);
        if (StrUtil.isNotBlank(json)){ //缓存存在
            Article article = JSONUtil.toBean(json, Article.class);
            if (BooleanUtil.isTrue(isMember)){
                article.setIsLike(1);
            }
            return ResModel.success(Code.SUCCESS,article);
        }
        //命中空值
        if (json!=null){
            return ResModel.error(Code.NON_EXISTENT);
        }
        String lockKey=MUTEX_ARTICLE_KEY+id;
        Article article=null;
        try {
            boolean b = tryLock(lockKey);
            if (!b){
                return getArticle(id);
            }
            article = getById(id);
            if (BooleanUtil.isTrue(isMember)){
                article.setIsLike(1);
            }
            if (article==null){
                // 缓存空值解决 缓存穿透
                redisTemplate.opsForValue().set(key,"",Duration.ofMinutes(2));
                return ResModel.error(Code.NON_EXISTENT);
            }
            //查询作者信息
            Integer uid = article.getUid();
            ResModel<User> userInfo = userService.getUserInfo(uid);
            article.setNickname(userInfo.getData().getNickname());
            article.setAvatar(userInfo.getData().getAvatar());
            //查询分类信息
            Integer cid = article.getCid();
            Classify classify = classifyMapper.selectById(cid);
            article.setArticleCategory(classify.getClassifyName());
            //查询标签信息
            String tagString = article.getArticleTag();
            String[] tags = tagString.split(",");
            List<Tag> tagList = new ArrayList<>();
            for (String s : tags) {
                Tag tag = tagMapper.selectById(Integer.parseInt(s));
                tagList.add(tag);
            }
            article.setTagList(tagList);
            //添加缓存
            redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(article), Duration.ofMinutes(30));
        }catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            //释放锁
            unlock(lockKey);
        }
        return ResModel.success(Code.SUCCESS,article);
    }
    /**
     * 获得锁
     * @param key
     * @return
     */
    private boolean tryLock(String key) {
        Boolean flag = redisTemplate.opsForValue().setIfAbsent(key, "2", Duration.ofSeconds(10));
        return BooleanUtil.isTrue(flag);
    }

    /**
     * 释放锁
     * @param key
     */
    private void unlock(String key) {
        redisTemplate.delete(key);
    }

    /**
     * 更新浏览量
     * @param id
     * @return
     */
    @Override
    @Transactional
    public ResModel addViews(Integer id) {
        if (id==null){
            return ResModel.error(Code.NON_EXISTENT);
        }
        //更新文章浏览次数
        update().setSql("views = views + 1").eq(true,"id",id).update();
        //更新网站浏览次数
        webService.update().setSql("visits = visits +1").update();
        return ResModel.success(Code.SUCCESS,id);
    }

    /**
     * 更新点赞量
     * @param id
     * @param uid
     * @return
     */
    @Override
    public ResModel likes(Integer id, Integer uid) {
        // 判断当前登录用户是否已经点赞
        String key = BLOG_LIKED_KEY + id;
        Boolean isMember = redisTemplate.opsForSet().isMember(key, uid.toString());
        if(BooleanUtil.isFalse(isMember)){
            // 如果未点赞，可以点赞
            // 数据库点赞数+1
            boolean isSuccess = update().setSql("likes = likes + 1").eq("id", id).update();
            // 保存用户到Redis的set集合
            if(isSuccess){
                redisTemplate.opsForSet().add(key,uid.toString());
            }
            //更新网站点赞次数
            webService.update().setSql("likes = likes + 1").update();
            return ResModel.success(Code.SUCCESS,1);
        }else {
            // 如果已点赞，取消点赞
            // 数据库点赞数-1
            boolean isSuccess = update().setSql("likes = likes - 1").eq("id", id).update();
            // 把用户从Redis的set集合移除
            if (isSuccess) {
                redisTemplate.opsForSet().remove(key, uid.toString());
            }
            //更新网站点赞次数
            webService.update().setSql("likes = likes - 1").update();
        }
        return ResModel.success(Code.SUCCESS,0);
    }

    /**
     * 查询推荐文章
     *
     * @param cid
     * @param id
     * @return
     */
    @Override
    public ResModel<List<Article>> getRecommend(Integer cid, Integer id) {
        if (id==null || cid==null){
            return ResModel.error(Code.FAIL);
        }
        //查询推荐文章
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(true,Article::getCid,cid);
        wrapper.ne(true,Article::getId,id);
        List<Article> list = list(wrapper);
        return ResModel.success(Code.SUCCESS,list);
    }

    /**
     * 发布文章
     * @param article
     * @return
     */
    @Override
    public ResModel issueArticle(Article article) {
        if (article==null || article.getCid()==null || article.getUid()==null){
            return ResModel.error(Code.UN_ISSUE);
        }
        article.setCreateTime(NowDateUtils.getNowDate());
        String join = StrUtil.join(",", article.getValues());
        article.setArticleTag(join);
        boolean save = save(article);
        return ResModel.success(Code.ISSUE,save);
    }

    /**
     * 编辑文章
     * @param article
     * @return
     */
    @Override
    public ResModel updateArticle(Article article) {
        if (article.getId()==null){
            return ResModel.error(Code.FAIL);
        }
        String join = StrUtil.join(",", article.getValues());
        article.setArticleTag(join);
        boolean b = updateById(article);
        return ResModel.success(Code.UPDATE,b);
    }

    /**
     * 查询热门文章
     * @return
     */
    @Override
    public ResModel<List<Article>> getHotArticle() {
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        wrapper.orderByDesc(true, Article::getViews).last("limit 0,8");
        List<Article> list = list(wrapper);
        return ResModel.success(Code.SUCCESS,list);
    }

    /**
     * 分页查询用户文章列表
     * @param current
     * @param size
     * @param uid
     * @return
     */
    @Override
    public ResModel<Page<Article>> getListByUid(Integer current, Integer size, Integer uid) {
        Page<Article> page = new Page<>(current, size);
        LambdaQueryWrapper<Article> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(true,Article::getUid,uid);
        Page<Article> articlePage = baseMapper.selectPage(page, wrapper);
        return ResModel.success(Code.SUCCESS,articlePage);
    }
}
